/**
 * Pangolin IM Server
 * autor:   yiyefangzhou24
 * email:   yiyefangzhou24@qq.com
 * data:    21/10/1
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include "event2/event.h"
#include "event2/listener.h"

#include "config.h"
#include "package.h"
#include "receive.h"
#include "db_mysql.h"
#include "db_redis.h"
#include "log.h"
#include "function.h"
#include "pop_message.h"

#define CONFIG_PATH "./etc/config"

void bevErrorCB(struct bufferevent *bev, short events, void *ctx){
    //这将自动close套接字和free读写缓冲区
    evutil_socket_t fd;
    fd = bufferevent_getfd(bev);
    close(fd);
    bufferevent_free(bev);
    LOG("client disconnected : %d\n" , fd);

    //清除redis中的客户端信息
    if(_response_removeclient((int)fd)){
        LOG("_response_removeclient : success : %d\n" , fd);
    }else{
        LOG("_response_removeclient  : nothing to do : %d\n" , fd);
    }
}


void bevWriteCB(struct bufferevent *bev, void *ctx){
    //printf("%s\n", "进入了Write回调");
}


void bevReadCB(struct bufferevent *bev, void *ctx){
    /* 循环读取bufferevent中的数据 */
    char package_buff[PACKAGE_BUFF_MAX_LEN];
    while(1){
        /* 清空包长缓冲 */
        memset(package_buff,0x00,PACKAGE_BUFF_MAX_LEN);

        /* 从bufferevent缓冲池中读取4字节数据 */
        size_t ret = bufferevent_read(bev,package_buff,4);
        /* 如果bufferevent缓冲池中没有数据则跳出循环 */
        if(ret < 4) break;

        /* 转换网络字节序的到包长 如果超出每个包最大限制视为无效包 */
        uint32_t net_package_len;
        memcpy(&net_package_len,package_buff,4);
        size_t package_len = ntohl(net_package_len);
        if(package_len > PACKAGE_BUFF_MAX_LEN || package_len < PACKAGE_HEAD_LEN){
            continue;
        }

        /* 读取整个包内容 */
        size_t tmp_len = package_len - 4;
        size_t ret_package_buff_len = bufferevent_read(bev,package_buff + 4,tmp_len);
        if(ret_package_buff_len < tmp_len){
            continue;
        }

        /* 调度协议命令 */
        receive_buff(bev,package_buff);
    }
}


void onAccept(struct evconnlistener *listener,evutil_socket_t fd,struct sockaddr *addr,int len, void *ptr){
    struct event_base *evBase = evconnlistener_get_base(listener);
    //设置socket为非阻塞
    evutil_make_socket_nonblocking(fd);
    //添加读写事件回调
    struct bufferevent *bev = bufferevent_socket_new(evBase,fd,BEV_OPT_CLOSE_ON_FREE);
    bufferevent_setcb(bev,bevReadCB,bevWriteCB,bevErrorCB,NULL);
    bufferevent_enable(bev,EV_READ);
    LOG("client connected : %d\n" , fd);
}

int main(){

    /* 读取配置文件 */
    int config_chatserver_port = 0;
    char config_chatserver_mysqlhost[MAX_NAME] = { 0 };
    char config_chatserver_mysqluser[MAX_NAME] = { 0 };
    char config_chatserver_mysqlpasswd[MAX_NAME] = { 0 };
    char config_chatserver_mysqlname[MAX_NAME] = { 0 };
    int config_chatserver_mysqlport = 0;
    char config_chatserver_redishost[MAX_NAME] = { 0 };
    int config_chatserver_redisport = 0;

    //初始化LOG库
    LOG_INIT(true , "./logs/");

    if( GetConfigIntValue(CONFIG_PATH , "CHATSERVER" , "port" , &config_chatserver_port) ||
        GetConfigStringValue(CONFIG_PATH , "CHATSERVER" , "mysql_host" , config_chatserver_mysqlhost) ||
        GetConfigStringValue(CONFIG_PATH , "CHATSERVER" , "mysql_user" , config_chatserver_mysqluser) ||
        GetConfigStringValue(CONFIG_PATH , "CHATSERVER" , "mysql_passwd" , config_chatserver_mysqlpasswd) ||
        GetConfigIntValue(CONFIG_PATH , "CHATSERVER" , "mysql_port" , &config_chatserver_mysqlport) ||
        GetConfigStringValue(CONFIG_PATH , "CHATSERVER" , "mysql_name" , config_chatserver_mysqlname) ||
        GetConfigStringValue(CONFIG_PATH , "CHATSERVER" , "redis_host" , config_chatserver_redishost) ||
        GetConfigIntValue(CONFIG_PATH , "CHATSERVER" , "redis_port" , &config_chatserver_redisport)){
        LOG("读取配置信息错误，请检查 ../etc/config 配置是否错误！");
        return -1;
    }

    printf("\n\n** -------------- config ---------------- **\n\n");
    printf("config_chatserver_port:%d\n",config_chatserver_port);
    printf("config_chatserver_mysql_host:%s\n",config_chatserver_mysqlhost);
    printf("config_chatserver_mysql_user:%s\n",config_chatserver_mysqluser);
    printf("config_chatserver_mysql_passwd:%s\n",config_chatserver_mysqlpasswd);
    printf("config_chatserver_mysql_port:%d\n",config_chatserver_mysqlport);
    printf("config_chatserver_mysql_name:%s\n",config_chatserver_mysqlname);
    printf("config_chatserver_redis_host:%s\n",config_chatserver_redishost);
    printf("config_chatserver_redis_port:%d\n",config_chatserver_redisport);
    printf("\n** -------------------------------------- **\n\n\n");



    //初始化mysql数据库连接
    if(init_mysql_pool(config_chatserver_mysqlhost ,  \
                    config_chatserver_mysqlport ,  \
                    config_chatserver_mysqluser ,  \
                    config_chatserver_mysqlpasswd, \
                    config_chatserver_mysqlname) == false){
        LOG("main : init mysql error , make sure mysql server started and check config");
        close_mysql_pool();
        return 0;
    }

    //初始化redis数据库连接
    if(init_redis_pool(config_chatserver_redishost , config_chatserver_redisport) == false){
        LOG("main : init redis error , make sure mysql server started and check config");
        close_redis_pool();
        return 0;
    }

    //初始化系统，主要用于同步mysql和redis中数据等
    system_init();

    //初始化libevent的base
    struct event_base *evBase;
    evBase = event_base_new();

    //声明服务器网络地址结构体
    struct sockaddr_in sin;
    //初始化
    memset(&sin,0,sizeof(sin));
    //附值
    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = htonl(INADDR_ANY);
    sin.sin_port = htons(config_chatserver_port);

    //创建监听Socket
    struct evconnlistener *listener;
    listener = evconnlistener_new_bind(
            evBase,onAccept,
            NULL,
            LEV_OPT_CLOSE_ON_FREE|LEV_OPT_REUSEABLE,
            -1,
            (struct sockaddr *)&sin,
            sizeof(sin));


    //初始化消息订阅线程
    init_pop_message_thread(config_chatserver_redishost , config_chatserver_redisport);

    //循环事件
    event_base_dispatch(evBase);

    //清理环境
    evconnlistener_free(listener);
    event_base_free(evBase);

    close_mysql_pool();
    close_redis_pool();

    LOG_DESTORY();
    return 0;
}